package com.tbit.uqbike.service.business.tag.strategy.add;

import cn.hutool.core.collection.CollectionUtil;
import com.tbit.uqbike.enums.TagOperationType;
import com.tbit.uqbike.object.business.UserInfo;
import com.tbit.uqbike.object.pojo.Machine;
import com.tbit.uqbike.object.pojo.tag.MachineTag;
import com.tbit.uqbike.object.pojo.vo.BatchOperationMsgVO;
import com.tbit.uqbike.service.base.MachineService;
import com.tbit.uqbike.service.business.tag.MachineInfoValidationResult;
import com.tbit.uqbike.webmanager.dao.core.MachineDao;
import com.tbit.uqbike.webmanager.dto.TagBindDTO;
import com.tbit.uqbike.webmanager.resp.tag.TagResult;
import com.tbit.uqbike.webmanager.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 通过用户编号 userCode 添加标签
 *
 *
 * @author <a href="mailto:chen.chaoxiong@tbit.com.cn">ChenChaoXiong</a>
 * @since 2024/7/12
 */
@Component
public class AddByUserCodeStrategy extends AbstractFillInfoOperation{
    @Autowired
    private MachineService machineService;
    @Resource
    private MachineDao machineDao;

    @Override
    protected BatchOperationMsgVO<TagResult> addTag(TagBindDTO tagBind) {
        Integer brandId = tagBind.getBrandId();
        Integer accountId = tagBind.getAccountId();
        UserInfo userInfo = tagBind.getUserInfo();

        Set<String> userCodes = tagBind.getUserCodes();
        Set<Long> tagIds = tagBind.getTagIds();
        if (CollectionUtil.isEmpty(userCodes) || CollectionUtil.isEmpty(tagIds)) {
            return BatchOperationMsgVO.empty();
        }
        BatchOperationMsgVO<TagResult> results = new BatchOperationMsgVO<>();

        MachineInfoValidationResult<String> machineInfoValidationResult = validateUserCodes(userCodes, brandId, accountId, results);
        if (!validateAndFilterTags(tagIds, brandId, accountId, results) || !machineInfoValidationResult.isValid()) {
            return results;
        }
        Map<Integer,String> userCodeMap = machineInfoValidationResult.getMachineinfoMap();
        Set<Integer> machineIds = userCodeMap.keySet();
        List<MachineTag> addableMachineTags = caculateAddableMachineTags(machineIds, tagIds, brandId, accountId, userInfo,results);
        saveMachineTags(addableMachineTags, results);
        fillMachineInfo(results,userCodeMap);
        return results;
    }

    /**
     * 校验用户编码是否存在
     * 将不存在的用户编码放入异常结果集
     * 返回存在的用户编码对应的设备ID集合
     * @param userCodes
     * @param brandId
     * @param accountId
     * @param results
     * @return
     */
    private MachineInfoValidationResult<String> validateUserCodes(Set<String> userCodes, Integer brandId, Integer accountId, BatchOperationMsgVO<TagResult> results) {
        if (CollectionUtil.isEmpty(userCodes)) {
            return MachineInfoValidationResult.invalid();
        }

        Map<String, Object> param = new HashMap<>();
        param.put("accountId", accountId);
        param.put("userCodes", StringUtil.getArrayListString(new ArrayList<>(userCodes)));
        List<Machine> machines = machineDao.getByAccountIdAndUserCodes(param);

        if (CollectionUtil.isEmpty(machines)) {
            results.putAllAbnormalValue(
                    userCodes.stream()
                             .map(userCode ->
                                     TagResult.builder()
                                              .brandId(brandId)
                                              .accountId(accountId)
                                              .userCode(userCode)
                                              .success(false)
                                              .message("Machine does not exist")
                                              .build()
                                     )
                             .collect(Collectors.toList())
            );
            return MachineInfoValidationResult.invalid();
        }

        Set<String> existingUserCodes = machines.stream()
                                                .map(Machine::getUserCode)
                                                .collect(Collectors.toSet());
        Set<String> notExistsUserCodes = userCodes.stream()
                                                  .filter(userCode -> !existingUserCodes.contains(userCode))
                                                  .collect(Collectors.toSet());

        if (!notExistsUserCodes.isEmpty()) {
            results.putAllAbnormalValue(
                    notExistsUserCodes.stream()
                                      .map(userCode ->
                                              TagResult.builder()
                                                       .brandId(brandId)
                                                       .accountId(accountId)
                                                       .userCode(userCode)
                                                       .success(false)
                                                       .message("Machine does not exist")
                                                       .build()
                                              )
                                      .collect(Collectors.toList())
            );
        }

        Map<Integer,String> userCodeMap = machines.stream()
                                                  .collect(Collectors.toMap(Machine::getMachineId, Machine::getUserCode));

        return MachineInfoValidationResult.valid(userCodeMap);
    }

    @Override
    public TagOperationType getOperationType() {
        return TagOperationType.ADD_BY_USER_CODE;
    }

    @Override
    protected void fillMachineInfo(BatchOperationMsgVO<TagResult> results, Map<Integer, String> machineNoMap) {
        List<TagResult> allResults = new ArrayList<>();
        allResults.addAll(results.getSuccessList());
        allResults.addAll(results.getAbnormalList());
        allResults.addAll(results.getFailList());

        allResults.forEach(tagResult -> {
            Integer machineId = tagResult.getMachineId();
            if (machineNoMap.containsKey(machineId)) {
                tagResult.setUserCode(machineNoMap.get(machineId));
            }
        });
    }
}
